O presente exercício tem como objetivo testar a aplicação de uma metodologia de planejamento da força de trabalho para a saúde bucal.
Essa é uma metodologia baseada em parâmetros de razão populacional (ex.: 1,5 procedimentos curativos para a população coberta) e utiliza como referência o caderno de critérios e parâmetros para planejamento de serviços no âmbito do SUS (2017).
De acordo com o caderno de critérios e parâmetros temos algumas previsões sobre a necessidade de procedimentos. A imagem abaixo apresenta tais condições
Diante dessas informações, depreende-se que alguns dados são necessários para a modelagem:
Projeções populacionais: nesse caso, utilizaremos as projeções populacionais do IBGE a nível UF, por contarmos com dados até 2030
Cobertura de Estratégia de Saúde da Família Saúde Bucal: nesse caso, os dados foram acessados por meio do e-gestor AB
Parâmetros opcional Conforme conversamos certa vez com o Marcelo, seria interessante que o usuário inclua a apenas a população SUS dependente ou deixe todos indivíduos. Penso que no momento da seleção da região, o usuário possa marcar uma caixinha de seleção com a seguinte estruturação: População, opção 1) “Considerar somente população SUS dependente”, opção 2) “Considerar população completa”.
Ao longo deste código vou alertar sobre o uso de um ou de outro.
O código abaixo serve para capturar dados de beneficiários de plano de saúde no estado de Goiás no ano de 2021.
plano_saude <- sqlQuery(channel, 'SELECT * FROM "Analytics Layer".Territorial."Beneficiários de plano de saúde por região de saúde"') %>%
group_by(cod_regsaud, regiao_saude) %>%
summarise(beneficiarios = sum(benef_assistencia_medica),
populacao = sum(populacao)) %>%
mutate(sus_dependente = beneficiarios/populacao) %>%
mutate(uf = substr(cod_regsaud, 1, 2)) %>%
filter(uf == "52")
Para essa metodologia serão necessários dados de população e da cobertura de estratégia de saúde - saúde bucal.
Primeiro vamos pegar a população
df_sb <- sqlQuery(channel, 'SELECT *
FROM "Analytics Layer".Territorial."População SVS por região de saúde e faixa etária"
WHERE ANO = 2021') %>%
filter(uf == "Goiás")
df_cobertura <- read_delim("saude_bucal/coberturasb_go.csv",
";", escape_double = FALSE, trim_ws = TRUE)
DT::datatable(df_cobertura)
df_sb <-
df_sb %>%
select(-uf, -regiao_saude) %>%
left_join(plano_saude, by = c("cod_regsaud"="cod_regsaud")) %>%
mutate(pop_sus_dependente = POP * (1 - sus_dependente))
Vamos utilizar o parâmetro de 24%. Porém, esse é um valor que deverá ser parametrizado no sistema.
Adicionei a letra “a” para identificar as seções que calculam a partir da população total.
Para fins de prototipação, vamos pegar só dados de Goiás
a <- df_sb %>%
ggplot(aes(x = FAIXA_ETARIA, y = POP, fill = regiao_saude)) + geom_col() +
coord_flip() + theme_minimal()
plotly::ggplotly(a)
A cobertura foi estabelecida em 0.24 (24%) visto que, multiplicando-se a taxa de cobertura da eSFSB e o SB2010 (48%), tivemos esse valor aproximado nos últimos cinco anos. No entanto, como dito anteriormente, essa cobertura pode ser ajustada.
df_sb0_14 <-
df_sb %>%
filter(FAIXA_ETARIA == "0 a 14") %>%
mutate(cobertura = POP * 0.24) %>%
mutate(proced_curativos = 1.5 * cobertura,
cobertura_endodontia = 0.08 * POP,
proced_endodontia = 0.1 * cobertura_endodontia)
DT::datatable(df_sb0_14 %>% select(-starts_with("cobertura")))
df_sb15_29 <-
df_sb %>%
filter(FAIXA_ETARIA == "15 a 29") %>%
mutate(cobertura = POP * 0.24) %>%
mutate(proced_curativos = 2.6 * cobertura,
cobertura_periodontia = 0.101 * POP,
proced_periodontia = 0.10 * cobertura_periodontia,
cobertura_cirurgia = 0.099 * POP,
proced_cirurgia = 0.20 * cobertura_cirurgia,
cobertura_endodontia = 0.1 * POP,
proced_endodontia = 0.1 * cobertura_endodontia,
cobertura_protese = 0.137 * POP,
proced_protese = 0.027 * cobertura_protese)
DT::datatable(df_sb15_29 %>% select(-starts_with("cobertura")))
df_sb30_59 <-
df_sb %>%
filter(FAIXA_ETARIA == "30 a 59") %>%
mutate(cobertura = POP * 0.24) %>%
mutate(proced_curativos = 4.1 * cobertura,
cobertura_periodontia = 0.179 * POP,
proced_periodontia = 0.12 * cobertura_periodontia,
cobertura_cirurgia = 0.164 * POP,
proced_cirurgia = 0.32 * cobertura_cirurgia,
cobertura_endodontia = 0.10 * POP,
proced_endodontia = 0.11 * cobertura_endodontia,
cobertura_protese = 0.687 * POP,
proced_protese = 0.138 * cobertura_protese)
DT::datatable(df_sb30_59 %>% select(-starts_with("cobertura")))
df_sb60 <-
df_sb %>%
filter(FAIXA_ETARIA == "60 ou mais") %>%
mutate(cobertura = POP * 0.24) %>%
mutate(proced_curativos = 1.64 * cobertura,
cobertura_periodontia = 0.039 * POP,
proced_periodontia = 0.04 * cobertura_periodontia,
cobertura_cirurgia = 0.107 * POP,
proced_cirurgia = 0.23 * cobertura_cirurgia,
cobertura_protese = 0.927 * POP,
proced_protese = 0.185 * cobertura_protese)
DT::datatable(df_sb60 %>% select(-starts_with("cobertura")))
Uma vez que é uma metodologia que se baseia especialmente na razão população, a estimativa de demanda por serviços de saúde se modifica de acordo com as dinâmicas demográficas. .
df_sb60t <- df_sb60 %>%
select(-uf, FAIXA_ETARIA, -starts_with("cobertura"), -POP) %>%
pivot_longer(
cols = starts_with("proced"),
names_to = "procedimento",
values_to = "total",
)
df_sb0_14t <- df_sb0_14 %>%
select(-uf, FAIXA_ETARIA, -starts_with("cobertura"), -POP) %>%
pivot_longer(
cols = starts_with("proced"),
names_to = "procedimento",
values_to = "total",
)
df_sb30_59t <- df_sb30_59%>%
select(-uf, FAIXA_ETARIA, -starts_with("cobertura"), -POP) %>%
pivot_longer(
cols = starts_with("proced"),
names_to = "procedimento",
values_to = "total",
)
df_sb15_29t <- df_sb15_29 %>%
select(-uf, FAIXA_ETARIA, -starts_with("cobertura"), -POP) %>%
pivot_longer(
cols = starts_with("proced"),
names_to = "procedimento",
values_to = "total",
)
df_total <-
rbind(df_sb0_14t, df_sb15_29t,
df_sb30_59t, df_sb60t) %>%
mutate(nivel_atencao = if_else(procedimento == "proced_curativos","primario",
"secundario"))
f <- df_total %>%
ggplot(aes(x = procedimento, y = total, fill = FAIXA_ETARIA)) + geom_col(position = "dodge") +
coord_flip() + theme_minimal()
plotly::ggplotly(f)
Procedimentos
DT::datatable(df_total)
Uma vez que se sabe o total de serviços necessários para endereçar a população considerando as taxas de coberturas e parâmetros do Ministério da Saúde, é possível determinar o número de horas necessário por profissional e, consequentemente, o total de profissionais em equivalente tempo integral (ETI).
O tempo dos procedimentos ficarão em uma escala de 20 minutos a 40 minutos.
O tempo dos procedimentos ficarão em uma escala de 20 minutos a 40 minutos.
df_total <-
df_total %>%
mutate(tempo_procedimento = case_when(procedimento == "proced_cirurgia" ~ 1,
procedimento == "proced_curativos" ~ 0.5,
procedimento == "proced_endodontia" ~ 0.5,
procedimento == "proced_periodontia" ~ 0.5,
procedimento == "proced_protese" ~ 0.66)) %>%
mutate(tempo_total = tempo_procedimento * total)
# assumimos o tempo máximo (40 minutoss, ou 0,66 horas), mas podemos reparametrizar
Agora é importante estimar o tempo médio de um profissional de saúde bucal ao longo de um ano. Considerando uma carga de trabalho de 40h semanais, temos 160 horas mensais. Considerando uma ausência de 40 dias ao ano devido a férias e outras possíveis ausências (atestados, licenças, atrasos), temos, ao longo de um ano 2600 horas de tempo disponível de um profissional.
demanda_fts_sb <-
df_total %>%
group_by(regiao_saude, nivel_atencao) %>%
summarise(tempo_total = sum(tempo_total, na.rm = TRUE)) %>%
mutate(tempo_total_mensal = tempo_total/12) %>%
mutate(fte_40_anual = tempo_total/2600) %>%
mutate(fte_40_mensal = fte_40_anual/12)
Primeiro vamos acessar a base que contém a hierarquia completa de municípios.
hierarquia <-
sqlQuery(channel, 'SELECT * FROM "Analytics Layer".Territorial."Municípios - Hierarquia Completa"')
fts_aps <-
sqlQuery(channel, "SELECT CODUFMUN, uf, TP_UNID, COMPETEN, CBO, HORA_AMB, HORAHOSP, HORAOUTR
FROM Dados.cnes.PF
WHERE uf = 'GO' AND substr(COMPETEN, 5, 2) = '12' AND CAST(substr(COMPETEN, 1, 4) AS INTEGER) = 2020 AND (TP_UNID = '01' OR TP_UNID = '02' OR TP_UNID = '40' OR
TP_UNID = '45' OR TP_UNID = '72')")
path_cbo_dentista <- "saude_bucal/cbo_dentista.csv"
cbo_dentista <- read_delim(path_cbo_dentista, ";", escape_double = FALSE, trim_ws = TRUE)
cbos <- cbo_dentista$codigo
cbo_dentista$codigo <- as.character(cbo_dentista$codigo)
dentistas_aps <-
fts_aps %>%
filter(CBO %in% cbos) %>%
left_join(cbo_dentista, by = c("CBO" = "codigo")) %>%
left_join(hierarquia, by = c("CODUFMUN" = "cod_municipio")) %>%
mutate(horas = HORA_AMB + HORAHOSP + HORAOUTR) %>%
group_by(uf_sigla, regiao_saude, tipo_categoria) %>%
summarise(total_horas_mensal = 4 * sum(horas)) %>%
mutate(horas_anual_oferta = total_horas_mensal * 12,
fte40_anual_oferta = horas_anual_oferta/2600,
fte40_mensal_oferta = fte40_anual_oferta/12,
nivel = "primario")
fts_ae <-
sqlQuery(channel,"SELECT CODUFMUN, uf, TP_UNID, COMPETEN, CBO,
HORA_AMB, HORAHOSP, HORAOUTR, PROF_SUS
FROM Dados.cnes.PF
WHERE substr(COMPETEN, 5, 2) = '12' AND
uf = 'GO' AND
CAST(substr(COMPETEN, 1, 4) AS INTEGER) = 2020 AND
(TP_UNID = '04' OR TP_UNID = '05' OR TP_UNID = '07' OR
TP_UNID = '15' OR TP_UNID = '22' OR TP_UNID = '36' OR
TP_UNID = '62' OR TP_UNID = '20' OR TP_UNID = '21')")
Em seguida, vou acessar apenas os CBOs associados à saúde bucal e filtrar
dentistas_ae <-
fts_ae %>%
filter(CBO %in% cbos) %>%
left_join(cbo_dentista, by = c("CBO" = "codigo")) %>%
left_join(hierarquia, by = c("CODUFMUN" = "cod_municipio")) %>%
mutate(horas = HORA_AMB + HORAHOSP + HORAOUTR) %>%
group_by(uf_sigla, regiao_saude, tipo_categoria) %>%
summarise(total_horas_mensal = 4 * sum(horas)) %>%
mutate(horas_anual_oferta = total_horas_mensal * 12,
fte40_anual_oferta = horas_anual_oferta/2600,
fte40_mensal_oferta = fte40_anual_oferta/12,
nivel = "secundario")
bases_oferta <-
rbind(dentistas_ae, dentistas_aps)
demanda_vs_oferta <- demanda_fts_sb %>%
left_join(bases_oferta, by = c("regiao_saude"="regiao_saude",
"nivel_atencao"="nivel")) %>%
filter(tipo_categoria == "Cirurgião Dentista") %>%
mutate(resultado = fte40_anual_oferta - fte_40_anual) %>%
mutate(resultado_quali = if_else(resultado > 0, "superavit", "deficit"))
demanda_vs_oferta %>%
ggplot(aes(x = regiao_saude, y = resultado, fill = resultado_quali)) +
geom_col() + coord_flip() + theme_minimal() + facet_grid(~nivel_atencao)
Adicionei a letra “b” para identificar as seções que calculam a partir da população SUS dependente.
Para fins de prototipação, vamos pegar só dados de Goiás
ab <- df_sb %>%
ggplot(aes(x = FAIXA_ETARIA, y = POP, fill = regiao_saude)) + geom_col() +
coord_flip() + theme_minimal()
plotly::ggplotly(ab)
A cobertura foi estabelecida em 0.24 (24%) visto que, multiplicando-se a taxa de cobertura da eSFSB e o SB2010 (48%), tivemos esse valor aproximado nos últimos cinco anos. No entanto, como dito anteriormente, essa cobertura pode ser ajustada.
df_sb0_14b <-
df_sb %>%
filter(FAIXA_ETARIA == "0 a 14") %>%
mutate(cobertura = pop_sus_dependente * 0.24) %>%
mutate(proced_curativos = 1.5 * cobertura,
cobertura_endodontia = 0.08 * pop_sus_dependente,
proced_endodontia = 0.1 * cobertura_endodontia)
DT::datatable(df_sb0_14b %>% select(-starts_with("cobertura")))
df_sb15_29b <-
df_sb %>%
filter(FAIXA_ETARIA == "15 a 29") %>%
mutate(cobertura = pop_sus_dependente * 0.24) %>%
mutate(proced_curativos = 2.6 * cobertura,
cobertura_periodontia = 0.101 * pop_sus_dependente,
proced_periodontia = 0.10 * cobertura_periodontia,
cobertura_cirurgia = 0.099 * pop_sus_dependente,
proced_cirurgia = 0.20 * cobertura_cirurgia,
cobertura_endodontia = 0.1 * pop_sus_dependente,
proced_endodontia = 0.1 * cobertura_endodontia,
cobertura_protese = 0.137 * pop_sus_dependente,
proced_protese = 0.027 * cobertura_protese)
DT::datatable(df_sb15_29b %>% select(-starts_with("cobertura")))
df_sb30_59b <-
df_sb %>%
filter(FAIXA_ETARIA == "30 a 59") %>%
mutate(cobertura = pop_sus_dependente * 0.24) %>%
mutate(proced_curativos = 4.1 * cobertura,
cobertura_periodontia = 0.179 * pop_sus_dependente,
proced_periodontia = 0.12 * cobertura_periodontia,
cobertura_cirurgia = 0.164 * pop_sus_dependente,
proced_cirurgia = 0.32 * cobertura_cirurgia,
cobertura_endodontia = 0.10 * pop_sus_dependente,
proced_endodontia = 0.11 * cobertura_endodontia,
cobertura_protese = 0.687 * pop_sus_dependente,
proced_protese = 0.138 * cobertura_protese)
DT::datatable(df_sb30_59b %>% select(-starts_with("cobertura")))
df_sb60b <-
df_sb %>%
filter(FAIXA_ETARIA == "60 ou mais") %>%
mutate(cobertura = pop_sus_dependente * 0.24) %>%
mutate(proced_curativos = 1.64 * cobertura,
cobertura_periodontia = 0.039 * pop_sus_dependente,
proced_periodontia = 0.04 * cobertura_periodontia,
cobertura_cirurgia = 0.107 * pop_sus_dependente,
proced_cirurgia = 0.23 * cobertura_cirurgia,
cobertura_protese = 0.927 * pop_sus_dependente,
proced_protese = 0.185 * cobertura_protese)
DT::datatable(df_sb60b %>% select(-starts_with("cobertura")))
Uma vez que é uma metodologia que se baseia especialmente na razão população, a estimativa de demanda por serviços de saúde se modifica de acordo com as dinâmicas demográficas. .
df_sb60tb <- df_sb60b %>%
select(-uf, FAIXA_ETARIA, -starts_with("cobertura"), -pop_sus_dependente) %>%
pivot_longer(
cols = starts_with("proced"),
names_to = "procedimento",
values_to = "total",
)
df_sb0_14tb <- df_sb0_14b %>%
select(-uf, FAIXA_ETARIA, -starts_with("cobertura"), -pop_sus_dependente) %>%
pivot_longer(
cols = starts_with("proced"),
names_to = "procedimento",
values_to = "total",
)
df_sb30_59tb <- df_sb30_59b %>%
select(-uf, FAIXA_ETARIA, -starts_with("cobertura"), -pop_sus_dependente) %>%
pivot_longer(
cols = starts_with("proced"),
names_to = "procedimento",
values_to = "total",
)
df_sb15_29tb <- df_sb15_29b %>%
select(-uf, FAIXA_ETARIA, -starts_with("cobertura"), -pop_sus_dependente) %>%
pivot_longer(
cols = starts_with("proced"),
names_to = "procedimento",
values_to = "total",
)
df_totalb <-
rbind(df_sb0_14tb, df_sb15_29tb,
df_sb30_59tb, df_sb60tb) %>%
mutate(nivel_atencao = if_else(procedimento == "proced_curativos","primario",
"secundario"))
fb <- df_totalb %>%
ggplot(aes(x = procedimento, y = total, fill = FAIXA_ETARIA)) + geom_col(position = "dodge") +
coord_flip() + theme_minimal()
plotly::ggplotly(fb)
Procedimentos
DT::datatable(df_totalb)
Uma vez que se sabe o total de serviços necessários para endereçar a população considerando as taxas de coberturas e parâmetros do Ministério da Saúde, é possível determinar o número de horas necessário por profissional e, consequentemente, o total de profissionais em equivalente tempo integral (ETI).
O tempo dos procedimentos ficarão em uma escala de 20 minutos a 40 minutos.
O tempo dos procedimentos ficarão em uma escala de 20 minutos a 40 minutos.
df_totalb <-
df_totalb %>%
mutate(tempo_procedimento = case_when(procedimento == "proced_cirurgia" ~ 1,
procedimento == "proced_curativos" ~ 0.5,
procedimento == "proced_endodontia" ~ 0.5,
procedimento == "proced_periodontia" ~ 0.5,
procedimento == "proced_protese" ~ 0.66)) %>%
mutate(tempo_total = tempo_procedimento * total)
# assumimos o tempo máximo (40 minutoss, ou 0,66 horas), mas podemos reparametrizar
Agora é importante estimar o tempo médio de um profissional de saúde bucal ao longo de um ano. Considerando uma carga de trabalho de 40h semanais, temos 160 horas mensais. Considerando uma ausência de 40 dias ao ano devido a férias e outras possíveis ausências (atestados, licenças, atrasos), temos, ao longo de um ano 2600 horas de tempo disponível de um profissional.
demanda_fts_sbb <-
df_totalb %>%
group_by(regiao_saude, nivel_atencao) %>%
summarise(tempo_total = sum(tempo_total, na.rm = TRUE)) %>%
mutate(tempo_total_mensal = tempo_total/12) %>%
mutate(fte_40_anual = tempo_total/2600) %>%
mutate(fte_40_mensal = fte_40_anual/12)
Primeiro vamos acessar a base que contém a hierarquia completa de municípios.
hierarquia <-
sqlQuery(channel, 'SELECT * FROM "Analytics Layer".Territorial."Municípios - Hierarquia Completa"')
fts_aps <-
sqlQuery(channel, "SELECT CODUFMUN, uf, TP_UNID, COMPETEN, CBO, HORA_AMB, HORAHOSP, HORAOUTR
FROM Dados.cnes.PF
WHERE uf = 'GO' AND substr(COMPETEN, 5, 2) = '12' AND CAST(substr(COMPETEN, 1, 4) AS INTEGER) = 2020 AND (TP_UNID = '01' OR TP_UNID = '02' OR TP_UNID = '40' OR
TP_UNID = '45' OR TP_UNID = '72')")
path_cbo_dentista <- "saude_bucal/cbo_dentista.csv"
cbo_dentista <- read_delim(path_cbo_dentista, ";", escape_double = FALSE, trim_ws = TRUE)
cbos <- cbo_dentista$codigo
cbo_dentista$codigo <- as.character(cbo_dentista$codigo)
dentistas_aps <-
fts_aps %>%
filter(CBO %in% cbos) %>%
left_join(cbo_dentista, by = c("CBO" = "codigo")) %>%
left_join(hierarquia, by = c("CODUFMUN" = "cod_municipio")) %>%
mutate(horas = HORA_AMB + HORAHOSP + HORAOUTR) %>%
group_by(uf_sigla, regiao_saude, tipo_categoria) %>%
summarise(total_horas_mensal = 4 * sum(horas)) %>%
mutate(horas_anual_oferta = total_horas_mensal * 12,
fte40_anual_oferta = horas_anual_oferta/2600,
fte40_mensal_oferta = fte40_anual_oferta/12,
nivel = "primario")
fts_aeb <-
sqlQuery(channel,"SELECT CODUFMUN, uf, TP_UNID, COMPETEN, CBO,
HORA_AMB, HORAHOSP, HORAOUTR, PROF_SUS
FROM Dados.cnes.PF
WHERE PROF_SUS = '1' AND
substr(COMPETEN, 5, 2) = '12' AND
uf = 'GO' AND
CAST(substr(COMPETEN, 1, 4) AS INTEGER) = 2020 AND
(TP_UNID = '04' OR TP_UNID = '05' OR TP_UNID = '07' OR
TP_UNID = '15' OR TP_UNID = '22' OR TP_UNID = '36' OR
TP_UNID = '62' OR TP_UNID = '20' OR TP_UNID = '21')")
Em seguida, vou acessar apenas os CBOs associados à saúde bucal e filtrar
dentistas_aeb <-
fts_ae %>%
filter(CBO %in% cbos) %>%
left_join(cbo_dentista, by = c("CBO" = "codigo")) %>%
left_join(hierarquia, by = c("CODUFMUN" = "cod_municipio")) %>%
mutate(horas = HORA_AMB + HORAHOSP + HORAOUTR) %>%
group_by(uf_sigla, regiao_saude, tipo_categoria) %>%
summarise(total_horas_mensal = 4 * sum(horas)) %>%
mutate(horas_anual_oferta = total_horas_mensal * 12,
fte40_anual_oferta = horas_anual_oferta/2600,
fte40_mensal_oferta = fte40_anual_oferta/12,
nivel = "secundario")
bases_ofertab <-
rbind(dentistas_aeb, dentistas_aps)
demanda_vs_ofertab <- demanda_fts_sbb %>%
left_join(bases_ofertab, by = c("regiao_saude"="regiao_saude",
"nivel_atencao"="nivel")) %>%
filter(tipo_categoria == "Cirurgião Dentista") %>%
mutate(resultado = fte40_anual_oferta - fte_40_anual) %>%
mutate(resultado_quali = if_else(resultado > 0, "superavit", "deficit"))
demanda_vs_ofertab %>%
ggplot(aes(x = regiao_saude, y = resultado, fill = resultado_quali)) +
geom_col() + coord_flip() + theme_minimal() + facet_grid(~nivel_atencao)